<?php

	/**
	 * Bike_Model_Mapper_Abstract
	 *
	 * Abstract class for mappers.
	 *
	 *
	 * @category   Bike
	 * @package    Bike_Model
	 * @subpackage Mapper
	 * @copyright  Copyright (c) 2012 DF-Studio. (http://www.df-studio.net)
	 * @version    1
	 */

	/**
	 * @see Bike_Initial
	 */
	require_once 'Bike/Initial.php';

	/**
	 * @see Bike_Model_Common
	 */
	require_once 'Bike/Model/Common.php';

	/**
	 * @see Bike_Model_DbTable_Common
	 */
	require_once 'Bike/Model/DbTable/Common.php';

	/**
	 * @see Zend_Db
	 */
	require_once 'Zend/Db.php';

	/**
	 * @see Bike_Db_Table
	 */
	require_once 'Bike/Db/Table.php';

	/**
	 * @see Bike_Model_Mapper_Dependence
	 */
	require_once 'Bike/Model/Mapper/Dependence.php';

	/**
	 * @see Zend_Reflection_Class
	 */
	require_once 'Zend/Reflection/Class.php';


	abstract class Bike_Model_Mapper_Abstract extends Bike_Initial
	{
        const URL_PARAM_NAME = 'id';

		const MAPPER_CLASS_NAME_PIECE = 'Mapper_';
        const PK_DELIM = Bike_Db_Table::PK_DELIM;
        const MODEL_CLASS = Null;
        const SEVE_IN_REGISTRY = False;
        const DEFAULT_MODEL_CLASS = 'Bike_Model_Common';
        const DB_TABLE_CLASS = 'Bike_Model_DbTable_Common';
        const DB_TABLE_NAME = Null;
        const DB_TABLE_PK_NAME = Null;
        const DB_TABLE_ALIAS = 'tab';
        const DB_TABLE_SCHEME = Null;
        const DB_TABLE_ADAPTER = Null;
        const DB_TABLE_LOCALIZATION = False;
        const DB_TABLE_SEQUENCE = True;
        const DB_TABLE_IDENTITY = Null;

        const DATA_TYPE_INT = Zend_Db::INT_TYPE;
        const DATA_TYPE_BIGINT = Zend_Db::BIGINT_TYPE;
        const DATA_TYPE_FLOAT = Zend_Db::FLOAT_TYPE;
        const DATA_TYPE_STRING = 4;

        const READ_ONLY = False;

        const DEFAULT_NONE     = Bike_Db_Table::DEFAULT_NONE;
    	const DEFAULT_CLASS    = Bike_Db_Table::DEFAULT_CLASS;
    	const DEFAULT_DB       = Bike_Db_Table::DEFAULT_DB;

    	const DEPENDENCES_INNER = Bike_Model_Mapper_Dependence::JOIN_INNER;
    	const DEPENDENCES_ALL   = 'All';

        const CALL_PREFIX_GET_STATUS_ID = 'getStatusId';
		const CALL_PREFIX_GET_STATUS = 'getStatus';

		const POSTFIX_FIELD_PARAMS 			= 'FieldParams';

		const PARAMS_COMMON 		= 'CommonParams';
		const PARAMS_LIST_COLUMN 	= 'ListColumnParams';
		const PARAMS_LIST_MODEL 	= 'ListModelParams';
		const PARAMS_FORM_ELEMENT 	= 'FormElementParams';
		const PARAMS_FORM_ELEMENT_INSERT 	= 'FormElementInsertParams';
		const PARAMS_FORM_ELEMENT_UPDATE 	= 'FormElementUpdateParams';

		const HANDLER_POSTFIX_BEFORE_INSERT 	= 'BeforeInsertHandler';
		const HANDLER_POSTFIX_AFTER_INSERT 		= 'AfterInsertHandler';
		const HANDLER_POSTFIX_BEFORE_UPDATE 	= 'BeforeUpdateHandler';
		const HANDLER_POSTFIX_AFTER_UPDATE 		= 'AfterUpdateHandler';
		const HANDLER_POSTFIX_BEFORE_DELETE		= 'BeforeDeleteHandler';
		const HANDLER_POSTFIX_AFTER_DELETE 		= 'AfterDeleteSaveHandler';

		const TASK_VIEW_ITEMS_PER_PAGE = 20;


        static protected $_instance = array();
        static protected $_numericDataTypes = array();

        protected $_dbTable;
        protected $_modelClass;
        protected $_dependences;
        protected $_readOnly;
        protected $_fieldsDataTypes;
        protected $_urlParamName;
        protected $_paramsNames;

        static public function js()
        {
        	return Bike_Js::getInstance();
        }

        static public function getInstance()
	    {
	        require_once 'Bike/Model/Mapper/Exception.php';
	    	throw new Bike_Model_Mapper_Exception('Static method "getIstance" must be declared in child class of '.__CLASS__);
	    }

	    static protected function _getInstance($ClassName)
	    {	    	if (null === @self::$_instance[$ClassName])
	    	{
	            self::$_instance[$ClassName] = new $ClassName();
	        }

	        return self::$_instance[$ClassName];
	    }

	    static public function getNumericDataTypes($className)
	    {
	    	if($className instanceof Zend_Db_Adapter_Abstract)
	    	{
	    		$className = get_class($className);
	    	}
	    	if(!isset(self::$_numericDataTypes[$className]))
	    	{
	    		$reflection = new Zend_Reflection_Class($className);
	    		$properties = $reflection->getDefaultProperties();
	    		self::$_numericDataTypes[$className] = $properties['_numericDataTypes'];

	    	}
	    	return self::$_numericDataTypes[$className];
	    }

	    public function __construct()
        {        	$ClassName = get_class($this);
        	if(@self::$_instance[$ClassName] !== null)
        	{        		require_once 'Bike/Model/Mapper/Exception.php';
        		throw new Bike_Model_Mapper_Exception('Use static method "'.$ClassName.'::getInstance"');
        	}
        	self::$_instance[$ClassName] = $this;
        	$this->init();
        }

        public function init()
        {
        	$this->_readOnly = $this->getConstReadOnly();
        }

        public function __call($MethodName, array $Args=null)
        {
        	if(preg_match("/^get([\w\d]+)(".join('|', array(
        		self::POSTFIX_FIELD_PARAMS,
        		self::HANDLER_POSTFIX_BEFORE_INSERT,
        		self::HANDLER_POSTFIX_AFTER_INSERT,
	        	self::HANDLER_POSTFIX_BEFORE_UPDATE,
	        	self::HANDLER_POSTFIX_AFTER_UPDATE,
	        	self::HANDLER_POSTFIX_BEFORE_DELETE,
	        	self::HANDLER_POSTFIX_AFTER_DELETE,
        		)).")$/s", $MethodName, $matches))
        	{
        		require_once 'Bike/Model/Mapper/Exception.php';
        		throw new Bike_Model_Mapper_Exception(sprintf('Undefined method %s', $MethodName));
        	}
        	if(preg_match("/^getHandlerPostfix([\w\d]+)$/s", $MethodName, $matches))
        	{
        		return $this->getConst(substr($MethodName, 3));
        	}
        	if(preg_match("/^getStatusId([\w\d]+)$/s", $MethodName, $matches))
        	{
        		return $this->getStatusId($matches[1], $Args);
        	}
        	if(preg_match("/^getStatus([\w\d]+)$/s", $MethodName, $matches))
        	{
        		return $this->getStatus($matches[1], $Args);
        	}
        	return parent::__call($MethodName, $Args);
        }

        protected function getFromRegistry()
        {        	if($this->getConstSaveInRegistry())
        	{
        		if(!Zend_Registry::isRegistered(__CLASS__))
		    	{
		    		Zend_Registry::set(__CLASS__, $this->fetchAll());
		    	}
		    	return Zend_Registry::get(__CLASS__);
			}
			require_once 'Bike/Model/Mapper/Exception.php';
        	throw new Bike_Model_Mapper_Exception("Constant ".get_class($this)."::SAVE_IN_REGISTRY is sated to False");
        }

        public function setUrlParamName($name)
        {
        	$this->_urlParamName = $name;
        	return $this;
        }

        public function getUrlParamName()
        {
        	if(is_null($this->_urlParamName))
        	{
        		$this->_urlParamName = $this->getConstUrlParamName();
        	}
        	return $this->_urlParamName;
        }

        public function getDefaultValues()
	    {
	    	return array();
	    }

	    public function getAllDefaultValues($DefaultSource=null)
	    {	    	if(!in_array($DefaultSource, array(self::DEFAULT_CLASS, self::DEFAULT_DB, self::DEFAULT_NONE)))
	        {
	            $DefaultSource = self::DEFAULT_NONE;
	        }
	    	$defaultValues = array_merge((array) $this->getDbTable()->getDefaultValues($DefaultSource), (array) (($DefaultSource == self::DEFAULT_CLASS) ? $this->getDefaultValues() : array()));
	    	foreach($this->getDependences() as $dependence)
	    	{	    		if($dependence->isInner())
	    		{
	    			$defaultValues = array_merge($dependence->getDefaultValues($DefaultSource), $defaultValues);
	    		}
	    	}
	    	return (array) $defaultValues;
	    }

		public function getDbTable(array $Options=array())
		{			if(is_null($this->_dbTable))
			{
				$dbTableName = $this->getConstDbTableName();
				$dbTableScheme = $this->getConstDbTableScheme();
				$className = $this->getConstDbTableClass();
				if(!@$this->getRegistry($dbTableName) instanceof $className)
				{					$Options = array(
						Bike_Model_DbTable_Common::ADAPTER => $this->getConstDbTableAdapter(),
						Bike_Model_DbTable_Common::NAME => (($dbTableScheme) ? $dbTableScheme.'.' : '').$dbTableName,
						Bike_Model_DbTable_Common::PRIMARY => $this->getConstDbTablePkName(),
						Bike_Model_DbTable_Common::LOCALIZATION => $this->getConstDbTableLocalization(),
						Bike_Model_DbTable_Common::SEQUENCE => $this->getConstDbTableSequence(),
						Bike_Model_DbTable_Common::IDENTITY => $this->getConstDbTableIdentity(),
					) + $Options;
					$dbTable = new $className($Options);
					$this->setRegistry($dbTableName, $dbTable);
				}
				$this->_dbTable = $this->getRegistry($dbTableName);
			}
			return $this->_dbTable;
		}

		protected function _getDependences()
		{			return array();
		}

		public function getDependences($Type=self::DEPENDENCES_ALL)
		{
			switch($Type)
			{				case self::DEPENDENCES_INNER:
							$dependences = array();
							foreach($this->_getDependences() as $dependence)
							{	                            if($dependence->isInner())
	                            {									array_push($dependences, $dependence);
	                            }
							}
							return $dependences;
				case self::DEPENDENCES_ALL:
				default:
							return $this->_getDependences();
			}
		}

		public function getInnerDependences()
		{
			return $this->getDependences(self::DEPENDENCES_INNER);
		}

		protected function createDependence(array $Options)
		{
			if(!@$Options['parentMapperClass'])
			{
				$Options['parentMapperClass'] = get_class($this);
			}
			return new Bike_Model_Mapper_Dependence($Options);
		}

		public function getTableName()
		{			return $this->getDbTable()->getName();
		}

		public function getTablePrimary()
		{
			return $this->getDbTable()->getPrimary();
		}

		public function getTableRowClass()
		{			return $this->getDbTable()->getRowClass();
		}

		public function getTableRowsetClass()
		{
			return $this->getDbTable()->getRowsetClass();
		}

		public function getTableAlias()
		{
			return $this->getConstDbTableAlias();
		}

		public function getTableNameWithAlias()
		{
			return array($this->getTableAlias() => $this->getTableName());
		}

		public function getTableMetaData()
		{
			return $this->getDbTable()->getMetadata();
		}

		public function getTableLocalizedFields()
		{
			return $this->getDbTable()->getLocalizedFields();
		}

		public function getTableUndefinedLocalizedFields()
		{
			return $this->getDbTable()->getUndefinedLocalizedFields();
		}

		public function getTableColumns()
		{
			return $this->getDbTable()->getColumns();
		}

		public function getModelClass()
		{			if(is_null($this->_modelClass))
			{
				$modelClass = $this->getConstModelClass();
				if(!$modelClass)
				{					$modelClass = str_replace(self::MAPPER_CLASS_NAME_PIECE, '', get_class($this));
				}
				if(!@class_exists($modelClass))
				{
					$modelClass = $this->getConstDefaultModelClass();
				}
				$this->_modelClass = $modelClass;
			}
			return $this->_modelClass;
		}

		public function getModelRowsetClass()
		{
			if($this->getConstModelClass())
			{				$className = $this->getConstModelClass().'Rowset';
				if(@class_exists($className))
				{					return $className;
				}
			}
			return $this->getTableRowsetClass();
		}

		public function getAdapter()
		{			return $this->getDbTable()->getAdapter();
		}

		public function getReadOnly()
		{			return $this->_readOnly;
		}

		protected function setReadOnly()
		{
			$this->_readOnly = True;
			return $this;
		}

		protected function _select($WithFrom=True)
		{			$Select = new Bike_Db_Mapper_Select($this);
			if($WithFrom)
			{				$Select->from($this->getTableNameWithAlias());
			}
			return $Select;
		}

		public function select(array $Fields=null)
		{			if(sizeof($Fields))
			{				$cols = $this->getTableColumns();
				$fields = array();
				foreach($Fields as $alias => $fieldName)
				{					if(in_array($fieldName, $cols))
					{						$fields[$alias] = $fieldName;
					}
				}
				$Select = $this->_select(False);
				$Select->from($this->getTableNameWithAlias(), $fields);
				$Select->joinDependencesByFields($this->getDependences(), $Fields);
			}
			else
			{				$Select = $this->_select();
				$Select->joinInnerDependences($this->getInnerDependences());
			}
			return $Select;
		}

		/*
		$Mapper->selectFull(array(
        	'item' => array(
        		'returnFields' => array('date_create'),
        		'deepening' => 'module',
        	),
        	'item_module' => array(
        		'returnFields' => array('name'),
        		'deepening' => False,
        	),
        	'item_status' => array(
        		'usage' => False,
        	),
        ));
		*/

		public function selectFull(array $Options=null)
		{
			$Select = $this->_select();
			$Select->joinDependences($this->getDependences(), $Options);
			return $Select;
		}

		public function insert(array $Data)
		{			if($this->getReadOnly())
			{				require_once 'Bike/Model/Mapper/Exception.php';
				throw new Bike_Model_Mapper_Exception("Can not insert new data with mapper with readOnly is True");
			}
			$Model = $this->fetchNew();
			$Model->setDirtyData($Data);
			return $Model->save();
		}

		public function update(array $Data, $Rowset)
	    {
	        if($this->getReadOnly())
			{
				require_once 'Bike/Model/Mapper/Exception.php';
				throw new Bike_Model_Mapper_Exception("Can not update data with mapper with readOnly is True");
			}
	        if($Rowset instanceof Bike_Db_Mapper_Select)
	        {	        	$Rows = $this->fetchAll($Rowset);
	        }
	        elseif($Rowset instanceof Bike_Db_Table_Rowset)
	        {	        	$Rows = $Rowset;
	        }
	    	foreach($Rows as $Model)
	        {	        	$Model->setDirtyData($Data);
	        	$Model->save();
	        }
	    }

	    public function delete($Rowset)
	    {
	        if($this->getReadOnly())
			{
				require_once 'Bike/Model/Mapper/Exception.php';
				throw new Bike_Model_Mapper_Exception("Can not delete data with mapper with readOnly is True");
			}
	        if($Rowset instanceof Bike_Db_Mapper_Select)
	        {
	        	$Rows = $this->fetchAll($Rowset);
	        }
	        elseif($Rowset instanceof Bike_Db_Table_Rowset)
	        {
	        	$Rows = $Rowset;
	        }
	        foreach($Rows as $Model)
	        {
	        	$Model->delete();
	        }
	    }

	    /**
	     * Get rowset of finded rows
	     *
	     * @param mixed $primaryKey
	     * @return Bike_Db_Table_Rowset object
	     */
	    public function find()
	    {	    	$args = func_get_args();
	    	array_unshift($args, $this->select());
	    	return call_user_func_array(array($this, '_find'), $args);
	    }

	    /**
	     * Get rowset of finded rows
	     *
	     * @param mixed $primaryKey
	     * @return Bike_Db_Table_Rowset object
	     */
	    public function findFull()
	    {
	    	$args = func_get_args();
	    	array_unshift($args, $this->selectFull());
	    	return call_user_func_array(array($this, '_find'), $args);
	    }

	    protected function _find()
	    {	    	$args = func_get_args();
	    	$Select = array_shift($args);
	    	if(!$Select instanceof Bike_Db_Mapper_Select)
	    	{	    		require_once 'Bike/Model/Mapper/Exception.php';
	    		throw new Bike_Model_Mapper_Exception('Undefined Bike_Db_Mapper_Select object');
	    	}
	    	$primaryKeyLen = sizeof($this->getTablePrimary());
	    	foreach($args as $primaryKey)
	    	{	    		if(!is_array($primaryKey))
    			{
    				$primaryKey = array($primaryKey);
    			}
	    		if(is_array($primaryKey) && sizeof($primaryKey) != $primaryKeyLen)
    			{
    				require_once 'Bike/Model/Mapper/Exception.php';
    				throw new Bike_Model_Mapper_Exception('Incorrect count of columns for the primary key');
    			}
	    		$conditionPart = array();
	    		foreach($this->getTablePrimary() as $pos => $primaryKeyName)
	    		{
	    			$i = $pos - 1;
	    			array_push($conditionPart, $primaryKeyName.' = '.$this->getAdapter()->quote(((array_key_exists($primaryKeyName, $primaryKey)) ? $primaryKey[$primaryKeyName] : $primaryKey[$i])));
	    		}
	    		$Select->orWhere(join($conditionPart, ' '.Zend_Db_Select::SQL_AND.' '));
	    	}
	    	return $this->fetchAll($Select);
	    }

		public function fetchAll($Select=null)
		{
			if(!$Select instanceof Bike_Db_Mapper_Select)
			{
				$Options = array($Select);
				$Select = $this->selectFull($Options);
			}
			if(get_class($Select->getMapper()) != get_class($this))
			{				require_once 'Bike/Model/Mapper/Exception.php';
				throw new Bike_Model_Mapper_Exception("Class of mapper of Select and current class of mapper are not the same");
			}

			$rows = $this->correctRowsDataTypes($Select->query()->fetchAll());
			$data  = array(
	            'table'    => $this->getDbTable(),
	            'data'     => $rows,
	            'rowClass' => $this->getModelClass(),
	            'stored'   => true,
	            'readOnly' => $this->getReadOnly(),
	            'mapperClass' => get_class($this),
	        );

	        $rowsetClass = $this->getModelRowsetClass();
	        if (!class_exists($rowsetClass))
	        {
	            require_once 'Zend/Loader.php';
	            Zend_Loader::loadClass($rowsetClass);
	        }
	        return new $rowsetClass($data);
		}

	    public function fetchRow($Select=null)
	    {
	        if(!$Select instanceof Bike_Db_Mapper_Select)
			{
				$Options = (array)$Select;
				$Select = $this->select($Options);
			}
	        $Select->limit(1, $Select->getPart(Zend_Db_Select::LIMIT_OFFSET));
	        $rows = $this->correctRowsDataTypes($Select->query()->fetchAll());
	        if (sizeof($rows) == 0)
	        {
	            return null;
	        }
	        return $this->createRow(reset($rows), True, False);
	    }

	    public function fetchNew($DefaultSource = self::DEFAULT_CLASS)
	    {
	        $model = $this->createRow(array(), False, True);
	        if($DefaultSource == self::DEFAULT_CLASS)
	        {
	        	$model->setDirtyData($this->getDefaultValues());
	        }
	        return $model;
	    }

	    public function createRow(array $Data = array(), $Stored = False, $WithDefaults = True, $DefaultSource = null, $ReadOnly=False)
	    {
	        $config = array(
	            'table'  => $this->getDbTable(),
	            'data'   => $this->correctRowsDataTypes((($WithDefaults) ? array_merge($this->getAllDefaultValues($DefaultSource), $Data) : $Data)),
	            'readOnly' => (bool) max(array((int) $this->getReadOnly(), (int) $ReadOnly)),
	            'stored' => $Stored,
	            'mapperClass' => get_class($this),
	        );

	        $modelClass = $this->getModelClass();
	        if (!class_exists($modelClass)) {
	            require_once 'Zend/Loader.php';
	            Zend_Loader::loadClass($modelClass);
	        }
	        return new $modelClass($config);
	    }

	    public function _createModelFromCleanData($Data, $Stored=False, $ReadOnly=False)
		{
			return $this->createRow($Data, $Stored, False, $DefaultSource = null, $ReadOnly);
		}

	    public function getStatus($Param, array $Args=null)
	    {
	    	require_once 'Bike/Model/Mapper/Status.php';
	    	$Status = Bike_Model_Mapper_Status::getInstance()->getFromRegistry()->getRowByCode($Param);
	    	require_once 'Bike/Db/Table/Row.php';
	    	if(!$Status instanceof Bike_Db_Table_Row)
	    	{
	    		require_once 'Bike/Model/Mapper/Exception.php';
	    		throw new Bike_Model_Mapper_Exception('Undefined status with code: '.$Param);
	    	}
	    	return $Status;
	    }

	    public function getStatusId($Param, array $Args=null)
	    {
	    	$PrimaryKey = $this->getStatus($Param)->getPrimaryKey();
	    	return reset($PrimaryKey);
	    }

	    public function getLocalizedFieldName($cleanFieldName)
	    {
	    	$filter = new Zend_Filter();
	    	$filter->addFilter(new Zend_Filter_Word_CamelCaseToUnderscore())
	    		   ->addFilter(new Zend_Filter_StringToLower());
	    	$cleanFieldName = $filter->filter($cleanFieldName);
	    	try
	    	{
	    		return $this->getDbTable()->getLocalizedFieldName($cleanFieldName);
	    	}
	    	catch(Zend_Db_Table_Exception $e)
	    	{
	    		foreach($this->getDependences() as $dependence)
	    		{
	    			if($dependence->isDependentAlias($cleanFieldName))
	    			{
		    			return $dependence->getAliasPrefix().$dependence->getMapper()->getLocalizedFieldName($dependence->getFieldNameWithoutAlias($cleanFieldName));
	    			}
	    		}
	    		require_once 'Bike/Model/Mapper/Exception.php';
	    		throw new Bike_Model_Mapper_Exception(sprintf('Undefined localized field %s', $cleanFieldName));
	    	}
	    }

	    public function getLocalizedFieldPrefix($localizedField, $withLanguageCode = false)
	    {
	    	try
	    	{
	    		return $this->getDbTable()->getLocalizedFieldPrefix($localizedField, $withLanguageCode);
	    	}
	    	catch(Zend_Db_Table_Exception $e)
	    	{
	    		foreach($this->getDependences() as $dependence)
	    		{
	    			if($dependence->isDependentAlias($localizedField))
	    			{
	    				$result = $dependence->getMapper()->getLocalizedFieldPrefix($dependence->getFieldNameWithoutAlias($localizedField), $withLanguageCode);
    					if($withLanguageCode)
    					{
    						$prefix = $dependence->getAliasPrefix().array_shift($result);
    						array_unshift($result, $prefix);
    						return $result;
    					}
    					return $dependence->getAliasPrefix().$result;
	    			}
	    		}
	    		require_once 'Zend/Db/Table/Exception.php';
				throw new Zend_Db_Table_Exception(sprintf('Specified column "%s" is not localized', $localizedField));
	    	}
	    }

	    public function getViewItemsPerPage()
	    {
	    	return $this->getConstTaskViewItemsPerPage();
	    }

	    public function correctDataType($name, $value)
	    {
	    	$fieldsDataTypes = $this->getFieldsDataTypes();
	    	if(array_key_exists($name, $fieldsDataTypes))
	    	{
	    		switch($fieldsDataTypes[$name])
	    		{
	    			case self::DATA_TYPE_INT:
	    			case self::DATA_TYPE_BIGINT: 	$value = intval($value); break;
	    			case self::DATA_TYPE_FLOAT: 	$value = floatval($value); break;
	    			case self::DATA_TYPE_STRING:
	    		}
	    	}
	    	return $value;
	    }

	    protected function correctRowsDataTypes($rows)
	    {
	    	if(is_array($rows))
	    	{
	    		foreach($rows as $n => $row)
	    		{
	    			if(is_array($row))
	    			{
	    				foreach($row as $name => $value)
	    				{
	    					$rows[$n][$name] = $this->correctDataType($name, $value);
	    				}
	    			}
	    		}
	    	}
	    	return $rows;
	    }

	    protected function prepareLocalizedParams($params, $languageCode, $type, $prefix)
	    {
	    	$langPostfix = ' ('.$languageCode.')';
	    	$langPostfixLen = strlen($langPostfix);
	    	if($type == self::PARAMS_LIST_COLUMN)
	    	{
	    		if(substr($params['text'], (strlen($params['text']) - $langPostfixLen)) != $langPostfix)
	    		{
	    			$params['text'] .= $langPostfix;
	    		}
	    		if($languageCode != $this->getDbTable()->getLanguage())
	    		{
	    			$params['hidden'] = true;
	    		}
	    	}
	    	elseif($type == self::PARAMS_FORM_ELEMENT)
	    	{
	    		if(substr($params['label'], (strlen($params['label']) - $langPostfixLen)) != $langPostfix)
	    		{
	    			$params['label'] .= $langPostfix;
	    		}
	    	}

	    	return $params;
	    }

	    public function getWindowTitleAdd()
	    {
	    	return $this->translate('New document');
	    }

	    public function getWindowTitleEdit(Bike_Model_Abstract $model)
	    {
	    	return $model->getPrimaryKeyValue();
	    }

	    public function getWindowTitle(Bike_Model_Abstract $model = null)
	    {
	    	if(is_null($model))
	    	{
	    		return $this->getWindowTitleAdd();
	    	}
	    	return $this->getWindowTitleEdit($model);
	    }

	    protected function getActionFieldsHandlers(Bike_Model_Abstract $model, $actionMethodPostfix)
	    {
	    	$methods = get_class_methods($this);
    		foreach($methods as $method)
    		{
    			if(preg_match('/^get([\d\w]+)'.$actionMethodPostfix.'$/s', $method))
    			{
    				$this->$method($model);
    			}
    		}
	    }

	    public function getBeforeInsertHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_BEFORE_INSERT);
	    }

	    public function getAfterInsertHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_AFTER_INSERT);
	    }

	    public function getBeforeUpdateHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_BEFORE_UPDATE);
	    }

	    public function getAfterUpdateHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_AFTER_UPDATE);
	    }

	    public function getBeforeDeleteHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_BEFORE_DELETE);
	    }

	    public function getAfterDeleteHandler(Bike_Model_Abstract $model)
	    {
	    	$this->getActionFieldsHandlers($model, self::HANDLER_POSTFIX_AFTER_DELETE);
	    }

	    public function getBeforeAddFormElementsHandler(Bike_Form $form, array $params = null, $fieldNamePrefix = '')
	    {
	    }

	    public function getAfterAddFormElementsHandler(Bike_Form $form, array $params = null, $fieldNamePrefix = '')
	    {
	    }

	    public function getTaskUpdateDirtyDataHandler(Bike_Form $form, StdClass $dirtyData, $fieldNamePrefix = '')
	    {
	    	foreach($this->getInnerDependences() as $dependence)
	    	{
	    		$dependence->getMapper()->getTaskUpdateDirtyDataHandler($form, $dirtyData, $fieldNamePrefix.$dependence->getAliasPrefix());
	    	}
	    }

	    public function getNewForm()
	    {
	    	require_once 'Bike/Form.php';
	    	return new Bike_Form();
	    }

	    public function getForm()
	    {
	    	$form = $this->getNewForm();

	    	return $form;
	    }


	    /*  FOR ExtJs  */

	    public function getFieldsDataTypes()
	    {
	    	if(is_null($this->_fieldsDataTypes))
	    	{
	    		$this->_fieldsDataTypes = array();
	    		$numericDataTypes = $this->getNumericDataTypes($this->getAdapter());
	    		$metaData = $this->getTableMetaData();
	    		foreach($metaData as $fieldName => $data)
	    		{
	    			$dataType = $numericDataTypes[strtoupper($data['DATA_TYPE'])];
	    			if($dataType === Zend_Db::INT_TYPE)
	    			{
	    				$type = self::DATA_TYPE_INT;
	    			}
	    			elseif($dataType === Zend_Db::BIGINT_TYPE)
	    			{
	    				$type = self::DATA_TYPE_BIGINT;
	    			}
	    			elseif($dataType === Zend_Db::FLOAT_TYPE)
	    			{
	    				$type = self::DATA_TYPE_FLOAT;
	    			}
	    			else
	    			{
	    				$type = self::DATA_TYPE_STRING;
	    			}

	    			$this->_fieldsDataTypes[$fieldName] = $type;
	    		}

	    		foreach($this->getDependences() as $dependence)
	    		{
	    			$fieldsDataTypes = $dependence->getMapper()->getFieldsDataTypes();
	    			foreach($fieldsDataTypes as $name => $type)
	    			{
	    				$this->_fieldsDataTypes[$dependence->getReturnFieldAlias($name)] = $type;
	    			}
	    		}

	    	}
	    	return $this->_fieldsDataTypes;
	    }

	    public function getModelFieldJsType($type)
	    {
	    	switch($type)
	    	{
	    		case self::DATA_TYPE_BIGINT:
	    		case self::DATA_TYPE_INT: 		return 'int';
	    		case self::DATA_TYPE_FLOAT: 	return 'float';
	    		case self::DATA_TYPE_STRING:	return 'string';
	    		default:						return 'auto';
	    	}
	    }

	    protected function getParamsNames()
	    {
	    	if(is_null($this->_paramsNames))
	    	{
		    	$methods = get_class_methods($this);
		    	$paramsNames = array();
		    	$filterStripCamelCase = new Zend_Filter();
		    	$filterStripCamelCase->addFilter(new Zend_Filter_Word_CamelCaseToUnderscore())
		    						 ->addFilter(new Zend_Filter_StringToLower());

		    	foreach($methods as $method)
		    	{
		    		if(preg_match('/^get([\d\w]+)'.self::POSTFIX_FIELD_PARAMS.'$/s', $method, $matches))
		    		{
		    			$fieldName = $filterStripCamelCase->filter($matches[1]);
		    			try
		    			{
		    				$this->getLocalizedFieldName($fieldName);
		    			}
		    			catch(Bike_Model_Mapper_Exception $e)
		    			{
		    				$paramsNames[$matches[1]] = $fieldName;
		    			}
		    		}
		    	}

		    	$this->_paramsNames = $paramsNames;
	    	}
	    	return $this->_paramsNames;
	    }

	    public function getFieldParamsMethodName($filteredPart)
	    {
	    	return 'get'.$filteredPart.self::POSTFIX_FIELD_PARAMS;
	    }

	    public function getModelFields()
	    {
	    	$modelFields = array();

	    	foreach($this->getDependences() as $dependence)
	    	{
	    		$depModelFields = $dependence->getMapper()->getModelFields();
	    		foreach($depModelFields as $name => $params)
	    		{
	    			$modelFields[$dependence->getReturnFieldAlias($name)] = $params;
	    		}
	    	}

	    	$modelFields = array_merge($modelFields, array_fill_keys(array_merge($this->getParamsNames(), $this->getTableColumns()), array()));
	    	$dataTypes = $this->getFieldsDataTypes();

	    	$filter = new Zend_Filter_Word_UnderscoreToCamelCase();
	    	foreach($modelFields as $name => $params)
	    	{
	    		$params = array_merge($params, array(
		    		'name' => $name,
		    		'type' => $this->getModelFieldJsType($dataTypes[$name]),
		    		'primaryKey' => in_array($name, $this->getTablePrimary()) ? true : false,
	    		));

	    		try
	    		{
	    			$part = $this->getLocalizedFieldPrefix($name);
	    		}
	    		catch(Zend_Db_Table_Exception $e)
	    		{
	    			$part = $name;
	    		}
	    		$filteredPart = $filter->filter($part);
	    		$methodName = $this->getFieldParamsMethodName($filteredPart);

	    		try
	    		{
	    			$params = array_merge($params, $this->$methodName(self::PARAMS_LIST_MODEL));
	    		}
	    		catch(Bike_Model_Mapper_Exception $e)
	    		{
	    		}

	    		$modelFields[$name] = $params;

	    		if(isset($params['notUsed']) && $params['notUsed'])
	    		{
	    			unset($modelFields[$name]);
	    		}
	    	}

	    	return $modelFields;
	    }

	    public function getListColumnsDefaults()
	    {
	    	return array(
	    		'flex' => 1,
	    	);
	    }

	    public function getListColumns()
	    {
	    	$columns = array();

	    	foreach($this->getInnerDependences() as $dependence)
	    	{
	    		$depColumns = $dependence->getMapper()->getListColumns();
	    		foreach($depColumns as $name => $params)
	    		{
	    			$columns[$dependence->getReturnFieldAlias($name)] = $params;
	    		}
	    	}

	    	$columns = array_merge($columns, array_fill_keys(array_merge($this->getParamsNames(), $this->getTableColumns()), array()));

	    	$columnsOrder = array();

	    	$filter = new Zend_Filter_Word_UnderscoreToCamelCase();
	    	foreach($columns as $name => $params)
	    	{
	    		$languageCode = null;
	    		$order = 9999999;
	    		try
	    		{
	    			list($part, $languageCode) = $this->getLocalizedFieldPrefix($name, true);
	    		}
	    		catch(Zend_Db_Table_Exception $e)
	    		{
	    			$part = $name;
	    		}

	    		$params = array_merge($params, array(
	    			'dataIndex' => $name,
	    			'filterable' => true,
	    		));

	    		$filteredPart = $filter->filter($part);
	    		$methodName = $this->getFieldParamsMethodName($filteredPart);

	    		try
	    		{
	    			$commonParams = $this->$methodName(self::PARAMS_COMMON);
	    			if($commonParams['order'])
	    			{
	    				$order = $commonParams['order'];
	    			}
	    			$params['order'] = $order;
	    			$params['text'] = $commonParams['label'];
	    			if(isset($commonParams['notUsed']))
	    			{
	    				$params['notUsed'] = $commonParams['notUsed'];
	    			}
	    		}
	    		catch(Bike_Model_Mapper_Exception $e)
	    		{
	    			if(!$params['order'])
	    			{
	    				$params['order'] = $order;
	    			}
	    		}

	    		try
	    		{
	    			$params = array_merge($params, $this->$methodName(self::PARAMS_LIST_COLUMN));
	    		}
	    		catch(Bike_Model_Mapper_Exception $e)
	    		{
	    		}

	    		if($languageCode)
	    		{
	    			$params = $this->prepareLocalizedParams($params, $languageCode, self::PARAMS_LIST_COLUMN, $part);
	    		}

	    		$columns[$name] = $params;
	    		$columnsOrder[$name] = $params['order'];

	    		if(isset($params['notUsed']) && $params['notUsed'])
	    		{
	    			unset($columns[$name]);
	    			unset($columnsOrder[$name]);
	    		}
	    	}

	    	asort($columnsOrder);
	    	$sortedColumns = array();
	    	foreach($columnsOrder as $name => $order)
	    	{
	    		$sortedColumns[$name] = $columns[$name];
	    	}

	    	return $sortedColumns;
	    }

	   	public function getFormFieldsParams($insert = false)
	    {
	    	switch($insert)
	    	{
	    		case true: 	$actionType = self::PARAMS_FORM_ELEMENT_INSERT; break;
	    		default: 	$actionType = self::PARAMS_FORM_ELEMENT_UPDATE;
	    	}

	    	$fieldsParams = array();

	    	foreach($this->getInnerDependences() as $dependence)
	    	{
	    		$depFieldsParams = $dependence->getMapper()->getFormFieldsParams($insert);
	    		foreach($depFieldsParams as $name => $params)
	    		{
	    			$fieldsParams[$dependence->getReturnFieldAlias($name)] = $params;
	    		}
	    	}

	    	$fieldsParams = array_merge($fieldsParams, array_fill_keys(array_merge($this->getParamsNames(), $this->getTableColumns()), array()));

	    	$filter = new Zend_Filter_Word_UnderscoreToCamelCase();
	    	foreach($fieldsParams as $name => $params)
	    	{
	    		$languageCode = null;
	    		$order = 9999999;

	    		try
	    		{
	    			list($part, $languageCode) = $this->getLocalizedFieldPrefix($name, true);
	    		}
	    		catch(Zend_Db_Table_Exception $e)
	    		{
	    			$part = $name;
	    		}
	    		$filteredPart = $filter->filter($part);

	    		$methodName = $this->getFieldParamsMethodName($filteredPart);

	    		foreach(array(self::PARAMS_COMMON, self::PARAMS_FORM_ELEMENT, $actionType) as $type)
	    		{
	    			try
	    			{
	    				$params = array_merge($params, $this->$methodName($type));
	    			}
	    			catch(Bike_Model_Mapper_Exception $e)
	    			{
	    				break;
	    			}
	    		}

	    		if(!isset($params['order']))
	    		{
	    			$params['order'] = $order;
	    		}

	    		if($languageCode)
	    		{
	    			$params = $this->prepareLocalizedParams($params, $languageCode, self::PARAMS_FORM_ELEMENT, $part);
	    		}

	    		$fieldsParams[$name] = $params;
	    		if(isset($params['notUsed']) && $params['notUsed'])
	    		{
	    			unset($fieldsParams[$name]);
	    		}
	    	}

	    	return $fieldsParams;
	    }

	    public function getViewPanelButtons(Bike_DataManagement_ExtJs_Buttons $buttons)
	    {
	    }

	}